# import sys
# sys.path.append("..")
# sys.path.append("platform_h")

from simulation_onequeue import MicroserviceSystem
from multiprocessing import Pool
# from concurrent.futures import ThreadPoolExecutor
import pickle
import datetime
from util import *



class Parameter():
    def __init__(self):
        self.lamda_norm = 0.0001   #
        self.lamda_error = 0.01       #
        self.lamda_com = 8
        self.lamda_out = 1           #
        self.num_server = 15         #
        self.time_deadline = 2

def load_default_para():
    parameter_t=Parameter()
    return parameter_t

def run_once_sim(parameters):

    start_record_time=100
    ms_system=MicroserviceSystem(parameters.lamda_norm, parameters.lamda_error, parameters.lamda_com, parameters.lamda_out,
                                   parameters.num_server, parameters.time_deadline, start_record_time)
    one_cycle=1/parameters.lamda_norm+1/parameters.lamda_error
    ms_system.run(max(one_cycle*10,1000))
    _,sim_prob_time_error=ms_system.get_sim_reliability()
    print(f"complete sim:{parameters.num_server}_{parameters.lamda_out}_{parameters.lamda_norm}_{parameters.lamda_error}")
    return parameters, sim_prob_time_error

def run_once_math(parameters):
    start_record_time = 100
    ms_system = MicroserviceSystem(parameters.lamda_norm, parameters.lamda_error, parameters.lamda_com, parameters.lamda_out,
                                   parameters.num_server, parameters.time_deadline, start_record_time)
    _, math_prob_time_error = ms_system.get_math_reliability()
    print(f"complete math:{parameters.num_server}_{parameters.lamda_out}_{parameters.lamda_norm}_{parameters.lamda_error}")
    return parameters, math_prob_time_error


def threading_pool_dealing(task_args_list,repeat_num):
    global parrallel_num
    math_res=[]
    sim_res=[]
    # threading_pool = ThreadPoolExecutor(max_workers=10)
    pool=Pool(parrallel_num)
    for parameter_t in task_args_list:
        res = pool.apply_async(run_once_math, args=(parameter_t,))
        math_res.append(res)
        for _ in range(repeat_num):
            res = pool.apply_async(run_once_sim, args=(parameter_t,))
            sim_res.append(res)
            
    # all_math_completed=all(res.result() for res in math_res)
    # all_sim_completed=all(res.result() for res in sim_res)
    # if all_math_completed and all_sim_completed:
    #     threading_pool.shutdown()
    # else:
    #     print(f"not complete wrong!")
    #     exit(-1)
        
    return math_res, sim_res

def compare_math_vs_sim_num_server(out_file_name,specific_list,repeat_num):
    x_math_list = []
    y_math_list = []
    x_sim_list = []
    y_sim_list = []
    task_args_list = []
    num_server_list=specific_list
    for num_server_t in num_server_list:
        parameter_t = load_default_para()
        parameter_t.num_server=num_server_t
        task_args_list.append(parameter_t)
    math_res, sim_res=threading_pool_dealing(task_args_list,repeat_num)
    
    for res in math_res:
        parameter_t, value = res.get()
        x_math_list.append(parameter_t.num_server)
        y_math_list.append(value)

    for res in sim_res:
        parameter_t, value = res.get()
        x_sim_list.append(parameter_t.num_server)
        y_sim_list.append(value)

    with open(f"{get_output_absolute_path()}{out_file_name}", 'wb') as f:
        pickle.dump((x_math_list,y_math_list,x_sim_list,y_sim_list), f)
        
    return 


def compare_math_vs_sim_lamda_out(out_file_name,specific_list,repeat_num):
    x_math_list = []
    y_math_list = []
    x_sim_list = []
    y_sim_list = []
    task_args_list = []
    lamda_out_list=specific_list
    for lamda_out_t in lamda_out_list:
        parameter_t = load_default_para()
        parameter_t.lamda_out=lamda_out_t
        task_args_list.append(parameter_t)
    math_res, sim_res=threading_pool_dealing(task_args_list,repeat_num)
    for res in math_res:
        parameter_t, value = res.get()
        x_math_list.append(parameter_t.lamda_out)
        y_math_list.append(value)

    for res in sim_res:
        parameter_t, value = res.get()
        x_sim_list.append(parameter_t.lamda_out)
        y_sim_list.append(value)

    with open(f"{get_output_absolute_path()}{out_file_name}", 'wb') as f:
        pickle.dump((x_math_list,y_math_list,x_sim_list,y_sim_list), f)
    return


def compare_math_vs_sim_lamda_norm(out_file_name,specific_list,repeat_num):
    x_math_list = []
    y_math_list = []
    x_sim_list = []
    y_sim_list = []
    task_args_list = []
    lamda_norm_list=specific_list
    for lamda_norm_t in lamda_norm_list:
        parameter_t = load_default_para()
        parameter_t.lamda_norm=lamda_norm_t
        task_args_list.append(parameter_t)
    math_res, sim_res=threading_pool_dealing(task_args_list,repeat_num)
    for res in math_res:
        parameter_t, value = res.get()
        x_math_list.append(parameter_t.lamda_norm)
        y_math_list.append(value)

    for res in sim_res:
        parameter_t, value = res.get()
        x_sim_list.append(parameter_t.lamda_norm)
        y_sim_list.append(value)

    with open(f"{get_output_absolute_path()}{out_file_name}", 'wb') as f:
        pickle.dump((x_math_list,y_math_list,x_sim_list,y_sim_list), f)
        
    return

def compare_math_vs_sim_lamda_error(out_file_name,specific_list, repeat_num):
    x_math_list = []
    y_math_list = []
    x_sim_list = []
    y_sim_list = []
    task_args_list = []
    lamda_error_list=specific_list
    for lamda_error_t in lamda_error_list:
        parameter_t = load_default_para()
        parameter_t.lamda_error=lamda_error_t
        task_args_list.append(parameter_t)
    math_res, sim_res=threading_pool_dealing(task_args_list,repeat_num)
    for res in math_res:
        parameter_t, value = res.get()
        x_math_list.append(parameter_t.lamda_error)
        y_math_list.append(value)

    for res in sim_res:
        parameter_t, value = res.get()
        x_sim_list.append(parameter_t.lamda_error)
        y_sim_list.append(value)

    with open(f"{get_output_absolute_path()}{out_file_name}", 'wb') as f:
        pickle.dump((x_math_list,y_math_list,x_sim_list,y_sim_list), f)
    return





global parrallel_num
parrallel_num=10

if __name__ == '__main__':
    para_t=Parameter()
    version=f"v1.0-{para_t.lamda_norm}-{para_t.lamda_error}-{para_t.lamda_com}-{para_t.lamda_out}-{para_t.num_server}-{para_t.time_deadline}"
    # 记录代码开始时间
    now_time = datetime.datetime.now()
    formatted_time = now_time.strftime('%m_%d_%H_%M_%S')

    
    repeat_num=20
    
    specific_list=[i for i in range(10,21)]
    out_file_name = f"Exp_compare_math_vs_sim_num_server_{version}_{formatted_time}.data"
    compare_math_vs_sim_num_server(out_file_name,specific_list,repeat_num)
    
    specific_list=[i for i in range(1,11)]
    out_file_name = f"Exp_compare_math_vs_sim_lamda_out_{version}_{formatted_time}.data"
    compare_math_vs_sim_lamda_out(out_file_name,specific_list,repeat_num)

    specific_list=[i*0.0001 for i in range(1,11)]
    out_file_name = f"Exp_compare_math_vs_sim_lamda_norm_{version}_{formatted_time}.data"
    compare_math_vs_sim_lamda_norm(out_file_name,specific_list,repeat_num)
    
    specific_list=[i*0.01 for i in range(1,11)]
    out_file_name = f"Exp_compare_math_vs_sim_lamda_error_{version}_{formatted_time}.data"
    compare_math_vs_sim_lamda_error(out_file_name,specific_list,repeat_num)
    
    


    # fig, ax = plt.subplots(1, 1, figsize=(7, 6))
    # ax.plot(x_math_list,y_math_list)
    # ax.scatter(x_sim_list,y_sim_list)
    # plt.show()

